package org.huzhp.gateway.filter;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.HttpMethod;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.io.UnsupportedEncodingException;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @Description: 调式拦截器
 * @Author: huzp
 * @CreateDate: 2020/3/13 13:02
 * @Version: 1.0
 */
@Component
public class DebugGatewayFilterFactory extends CommonGatewayFilterFactory<DebugGatewayFilterFactory.Config> {


    @Autowired
    private CacheManager cacheManager;


    private static final String START_TIME = "startTime";


    public static String CACHE_USER_LOGIN_NAME = "DEBUG_LOG_CACHE";


    public DebugGatewayFilterFactory() {
        // 这里需要将自定义的config传过去，否则会报告ClassCastException
        super(Config.class);
    }

    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList("enable", "saveLog");
    }

    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {

            ServerHttpRequest request = exchange.getRequest();
            exchange.getAttributes().put(START_TIME, System.currentTimeMillis());
            StringBuffer info = new StringBuffer("\n\r----------------------进入调式模式----------------------");
            info.append("\n\r请求基本信息:")
                    .append(KONG).append("请求ID:").append(request.getId())
                    .append(KONG).append("请求时间:").append(LocalDateTime.now())
                    .append(KONG).append("请求地址:").append(request.getURI().toString())
                    .append(KONG).append("请求方法:").append(request.getMethod())
                    .append(KONG).append("远程地址:").append(request.getLocalAddress().toString());
            info.append("\n请求头:");
            request.getHeaders().forEach((k, v) -> info.append(KONG).append(k).append(":").append(v.stream().collect(Collectors.joining(","))));
            info.append("\n请求参数:");
            if (HttpMethod.GET.equals(request.getMethod())) {
                request.getQueryParams().forEach((k, v) -> info.append(KONG).append(k).append(":").append(v.stream().collect(Collectors.joining(","))));
                logger.info(info.toString());

                if (config.isSaveLog()) {
                    cacheDebugLog(request.getId(), info.toString());
                }
                return chain.filter(exchange).then(Mono.fromRunnable(() -> responseCallBack(config, exchange)));
            } else {
                return DataBufferUtils.join(exchange.getRequest().getBody())
                        .flatMap(dataBuffer -> {
                            byte[] bytes = new byte[dataBuffer.readableByteCount()];
                            dataBuffer.read(bytes);
                            try {
                                String bodyString = new String(bytes, "utf-8");
                                info.append(bodyString);
                                exchange.getAttributes().put(POST_BODY, bodyString);
                            } catch (UnsupportedEncodingException e) {
                            }
                            DataBufferUtils.release(dataBuffer);
                            Flux<DataBuffer> cachedFlux = Flux.defer(() -> {
                                DataBuffer buffer = exchange.getResponse().bufferFactory()
                                        .wrap(bytes);
                                return Mono.just(buffer);
                            });

                            ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator(
                                    exchange.getRequest()) {
                                @Override
                                public Flux<DataBuffer> getBody() {
                                    return cachedFlux;
                                }
                            };
                            logger.info(info.toString());
                            if (config.isSaveLog()) {
                                cacheDebugLog(request.getId(), info.toString());
                            }
                            return chain.filter(exchange.mutate().request(mutatedRequest)
                                    .build()).then(Mono.fromRunnable(() -> responseCallBack(config, exchange)));
                        });
            }
        };
    }


    private void cacheDebugLog(String requestId, String msg) {
        Cache cache = cacheManager.getCache(CACHE_USER_LOGIN_NAME);
        String cacheMsg = cache.get(requestId, String.class);
        if (cacheMsg == null) {
            cacheMsg = msg;
        } else {
            cacheMsg += msg;
        }
        cache.put(requestId, cacheMsg);
    }


    /**
     * 打印响应日志信息
     *
     * @param config
     * @param exchange
     */
    @Override
    protected void responseCallBack(Config config, ServerWebExchange exchange) {

        super.responseCallBack(config, exchange);

        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        Long startTime = exchange.getAttribute(START_TIME);
        StringBuffer info = new StringBuffer();
        setResponeHeader(request, response);
        info.append("\n响应基本信息:")
                .append(KONG).append("响应状态码:").append(response.getStatusCode().toString());
        if (startTime != null) {
            Long executeTime = (System.currentTimeMillis() - startTime);
            info.append(KONG).append("请求耗时:").append(executeTime).append("ms");
        }
        info.append("\n响应头:");
        response.getHeaders().forEach((k, v) -> info.append(KONG).append(k).append(":").append(v.stream().collect(Collectors.joining(","))));
        logger.info(info.toString());
        if (config.isSaveLog()) {
            cacheDebugLog(request.getId(), info.toString());
        }
    }


    public static class Config {
        //是否保存调式日志
        private boolean saveLog;

        public Config() {
        }


        public boolean isSaveLog() {
            return saveLog;
        }

        public void setSaveLog(boolean saveLog) {
            this.saveLog = saveLog;
        }

        @Override
        public String toString() {
            return "Config{" +
                    "saveLog=" + saveLog +
                    '}';
        }
    }
}
